/**
 * @author wItspirit
 * 28-mei-2005
 * RestFlickrApi.java
 */

package org.scohen.juploadr.uploadapi.flickrrest;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.prefs.Preferences;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Decorations;
import org.scohen.juploadr.app.ImageAttributes;
import org.scohen.juploadr.app.JUploadr;
import org.scohen.juploadr.app.PhotoSet;
import org.scohen.juploadr.app.UploadImage;
import org.scohen.juploadr.event.UploadCompleteEvent;
import org.scohen.juploadr.event.UploadStatusMonitor;
import org.scohen.juploadr.ui.MessageUtils;
import org.scohen.juploadr.ui.PopupHandler;
import org.scohen.juploadr.ui.ProgressDialog;
import org.scohen.juploadr.ui.ReusableUIFactory;
import org.scohen.juploadr.uploadapi.Account;
import org.scohen.juploadr.uploadapi.CommunicationException;
import org.scohen.juploadr.uploadapi.ImageUploadApi;
import org.scohen.juploadr.uploadapi.ProtocolException;
import org.scohen.juploadr.uploadapi.UploadFailedException;
import org.scohen.juploadr.uploadapi.abilities.Abilities;
import org.scohen.juploadr.uploadapi.abilities.OrganizeAbilityNotification;
import org.scohen.juploadr.uploadapi.flickrrest.authentication.FlickrAccount;
import org.scohen.juploadr.uploadapi.flickrrest.authentication.FlickrAuthEventDirector;
import org.scohen.juploadr.uploadapi.flickrrest.geo.SetLocationTask;
import org.scohen.juploadr.uploadapi.flickrrest.photosets.FlickrPhotoSet;
import org.scohen.juploadr.uploadapi.flickrrest.photosets.GetPhotosetList;
import org.scohen.juploadr.uploadapi.flickrrest.tags.GetListUser;
import org.scohen.juploadr.uploadapi.flickrrest.ui.FlickrForm;
import org.scohen.juploadr.uploadapi.flickrrest.ui.JUploadrPopupHandler;
import org.scohen.juploadr.uploadapi.flickrrest.ui.MetadataForm;
import org.scohen.juploadr.uploadapi.flickrrest.ui.NewPhotoSetDialog;
import org.scohen.juploadr.uploadapi.flickrrest.upload.FlickrUpload;
/**
 * This is the REST based implementation of the FlickrApi
 */
public class RestFlickrApi extends ImageUploadApi {
    public static final String FLICKR_SHARED_SECRET = "e45cca24a72f6eb9"; //$NON-NLS-1$
    public static final String FLICKR_API_KEY = "1892f5f9d32f01b86f53b50cdac89c8c"; //$NON-NLS-1$

    private Class[] configClasses = { FlickrForm.class, MetadataForm.class };
    private static Map<Class, List<PostUploadAction>> unfinishedActions = new HashMap<Class, List<PostUploadAction>>();
    private static Account currentAccount;
    private static final String DEFAULT_ACCOUNT_KEY = RestFlickrApi.class.getName() + ".defaultAccount"; //$NON-NLS-1$
    private static Preferences prefs = Preferences.userNodeForPackage(RestFlickrApi.class);

    private static final RestFlickrApi instance = new RestFlickrApi();

    /**
     * 
     */
    public RestFlickrApi() {
    }

    public static synchronized RestFlickrApi getInstance() {
        return instance;
    }

    public String getSiteName() {
        return "Flickr"; //$NON-NLS-1$
    }

    /**
     * @see org.scohen.juploadr.uploadapi.ImageUploadApi#upload(org.scohen.juploadr.uploadapi.UserInfo,
     *      org.scohen.juploadr.app.UploadImage,
     *      org.scohen.juploadr.event.UploadStatusMonitor)
     */
    public void upload(UploadImage imageAttributes, UploadStatusMonitor monitor) throws UploadFailedException,
            ProtocolException, CommunicationException {
        if (!isCurrentAccountAuthenticated()) {
            try {
                authenticate(getCurrentAccount());
            } catch (ProtocolException e) {
                throw new UploadFailedException(e.getMessage(), e);
            }
        }
        FlickrUpload flickrUpload = new FlickrUpload(imageAttributes, monitor);
        flickrUpload.execute();
    }

    public Account getCurrentAccount() {
        if (currentAccount == null) {
            Map accounts = FlickrAccount.allAccounts();
            if (currentAccount == null) {
                currentAccount = FlickrAccount.getNullAccount();
            }

            if (accounts.size() > 0 && accounts.get(prefs.get(DEFAULT_ACCOUNT_KEY, "")) != null) { //$NON-NLS-1$
                currentAccount = (Account) accounts.get(prefs.get(DEFAULT_ACCOUNT_KEY, "")); //$NON-NLS-1$

            }
        }
        return currentAccount;

    }

    public Account newAccount(String name) {
        return new FlickrAccount(name);
    }

    public void setCurrentAccount(Account account) {
        if (account != null && !(account instanceof FlickrAccount)) {
            throw new IllegalArgumentException(
                    Messages
                            .getString(
                                    "uploadapi.flickr.incorrect.account.type", account.getClass().getName(), FlickrAccount.class.getName())); //$NON-NLS-1$
        }

        currentAccount = account;
        if (currentAccount.getUsername() != null) {
            prefs.put(DEFAULT_ACCOUNT_KEY, currentAccount.getUsername());
        }
    }

    public List<Account> allAccounts() {
        return new LinkedList<Account>(FlickrAccount.allAccounts().values());
    }

    public boolean isCurrentAccountNullAccount() {
        return getCurrentAccount().equals(FlickrAccount.getNullAccount());
    }

    public boolean authenticate(Account account) throws ProtocolException, CommunicationException {
        FlickrAuthEventDirector eventDirector = new FlickrAuthEventDirector();
        return eventDirector.execute((FlickrAccount) account);
    }

    public void initialize() {
        if (isCurrentAccountAuthenticated()) {
            Thread getPhotoSets = new Thread() {
                public void run() {
                    // get their photosets
                    GetPhotosetList getPhotoSets = new GetPhotosetList();
                    try {
                        getPhotoSets.execute();
                    } catch (InvalidAuthTokenException ite) {
                        reauthCurrentAccount();
                    } catch (ProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (CommunicationException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };

            Thread getTags = new Thread() {
                public void run() {
                    Account account = ImageUploadApi.getInstance().getCurrentAccount();
                    GetListUser getTags = new GetListUser();
                    try {
                        getTags.execute();
                        account.setUserTags(getTags.getTags());
                        account.save();
                    } catch (ProtocolException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (CommunicationException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }
            };

            getPhotoSets.start();
            getTags.start();
        }
    }

    public PhotoSet newPhotoSet(ImageAttributes primaryPhoto) {
        NewPhotoSetDialog dialog = new NewPhotoSetDialog(ReusableUIFactory.getInstance().getDisplay()
                .getActiveShell(), primaryPhoto);
        dialog.open();
        return dialog.getPhotoSet();
    }

    @Override
    public PhotoSet newPhotoSet(String name, String description, ImageAttributes primaryPhoto) {
        FlickrPhotoSet set = new FlickrPhotoSet(name, description, primaryPhoto);
        ImageUploadApi.getInstance().getAbilities().getOrganizeAbilities().notified(new OrganizeAbilityNotification(set));
        return set;
    }

    public Class[] getConfiurationDialogs() {
        return configClasses;
    }

    public PopupHandler windowRightClicked(Decorations parent, JUploadr clicked) {
        return new JUploadrPopupHandler(parent, clicked, ImageUploadApi.getInstance().getAbilities().getOrganizeAbilities()
                .getPhotoSets());
    }

    public PopupHandler thumbnailRightClicked(Decorations parent, JUploadr uploadr, ImageAttributes clicked) {
        // if the image isn't selected, de-select any selected images
        // and select this one.
        if (!clicked.isSelected()) {
            Collection allAttributes = uploadr.getImageAttributes().values();
            Iterator iter = allAttributes.iterator();
            while (iter.hasNext()) {
                ImageAttributes atts = (ImageAttributes) iter.next();
                atts.setSelected(atts.equals(clicked));
            }
        }
        uploadr.refresh();
        return new JUploadrPopupHandler(parent, uploadr, ImageUploadApi.getInstance().getAbilities().getOrganizeAbilities()
                .getPhotoSets());
    }

    public void postProcessUpload(UploadCompleteEvent e, ImageAttributes atts) {
        atts.setPhotoId(e.getResponse().getPhotoId());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.scohen.juploadr.uploadapi.ImageUploadApi#postProcessAllUploads(java.util.Collection)
     */
    public void postProcessAllUploads(Collection<? extends UploadImage> images) {
        // Iterator iter = photoSets.iterator();
        List<? extends PhotoSet> photoSets = ImageUploadApi.getInstance().getAbilities().getOrganizeAbilities().getPhotoSets();
        CreateAllPhotosetsTask createTask = new CreateAllPhotosetsTask(photoSets, this);
        ProgressDialog pd;
        if (createTask.hasSetsToCreate()) {
            pd = new ProgressDialog(JUploadr.window, createTask);
            pd.execute();
        }

        AddToPhotosetTask addTask = new AddToPhotosetTask(photoSets, this);
        if (addTask.hasSetsToAdd()) {
            pd = new ProgressDialog(JUploadr.window, addTask);
            pd.execute();
        }
        rotateImages(images);

        SetLocationTask locationTask = new SetLocationTask(images);
        if ( locationTask.hasPhotosToGeoTag()) {
            pd = new ProgressDialog(JUploadr.window, locationTask);
            pd.execute();
        }
        
        while (unfinishedActions.size() > 0 && showUnfinishedUploadErrorMessage()) {
            retryUnfinishedActions();
        }

        // ok, since we've updated the photosets, re-get them to make sure we're
        // in sync
        photoSets.clear();
        GetPhotosetList getList = new GetPhotosetList();
        executeMethod(getList);

    }

    private void retryUnfinishedActions() {
        List<PostUploadAction> actions = new LinkedList<PostUploadAction>();
        Iterator iter = unfinishedActions.values().iterator();
        for (List<PostUploadAction> next : unfinishedActions.values()) {
            actions.addAll(next);
        }
        unfinishedActions.clear();
        iter = actions.iterator();
        while (iter.hasNext()) {
            FlickrMethod method = (FlickrMethod) iter.next();
            executeMethod(method);
        }
    }

    /**
     * 
     */
    private boolean showUnfinishedUploadErrorMessage() {
        StringBuffer errorMessage = new StringBuffer();
        errorMessage.append(Messages.getString("juploadr.flickerrest.error.after.upload")); //$NON-NLS-1$
        Iterator failureClasses = unfinishedActions.keySet().iterator();
        while (failureClasses.hasNext()) {
            List actions = getFailedActions((Class) failureClasses.next());
            if (actions != null) {
                Iterator actionIter = actions.iterator();
                while (actionIter.hasNext()) {
                    PostUploadAction method = (PostUploadAction) actionIter.next();
                    errorMessage.append(Messages.getString("juploadr.flickerrest.failed.to")); //$NON-NLS-1$
                    errorMessage.append(method.getErrorText());
                    errorMessage.append("\n"); //$NON-NLS-1$
                }
            }

        }
        errorMessage.append(Messages.getString("juploadr.flickerrest.option.retry")); //$NON-NLS-1$
        return MessageUtils.showMessage(errorMessage.toString(), Messages
                .getString("juploadr.flickerrest.actions.failed"), SWT.ICON_WARNING | SWT.YES | SWT.NO); //$NON-NLS-1$

    }

    private void rotateImages(Collection<? extends UploadImage> images) {
        final Collection coll = getImagesToRotate(images);
        if (coll.size() > 0) {
            RotateTask rt = new RotateTask(coll, this);
            ProgressDialog pd = new ProgressDialog(JUploadr.window, rt);
            pd.execute();
        }
    }

    private void addFailedAction(PostUploadAction failed) {
        List<PostUploadAction> failedActions = unfinishedActions.get(failed.getClass());
        if (failedActions == null) {
            failedActions = new LinkedList<PostUploadAction>();
        }
        if (!failedActions.contains(failed)) {
            failedActions.add(failed);
        }
        unfinishedActions.put(failed.getClass(), failedActions);
    }

    private List getFailedActions(Class clazz) {
        return (List) unfinishedActions.get(clazz);
    }

    private Collection<UploadImage> getImagesToRotate(Collection<? extends UploadImage> allImages) {
        List<UploadImage> images = new LinkedList<UploadImage>();
        Iterator iter = allImages.iterator();
        while (iter.hasNext()) {
            UploadImage image = (UploadImage) iter.next();
            if (image.getRotation() != 0) {
                images.add(image);
            }
        }
        return images;
    }

    void reauthCurrentAccount() {
        if (!isCurrentAccountNullAccount()) {
            getCurrentAccount().setAuthenticated(false);
            ReusableUIFactory.getInstance().getDisplay().syncExec(new Runnable() {
                public void run() {
                    try {
                        authenticate(getCurrentAccount());
                    } catch (ProtocolException e) {
                        // oh boy, this is terrible.
                        e.printStackTrace();
                    } catch (CommunicationException e) {
                        MessageUtils.showErrorMessage(org.scohen.juploadr.Messages
                                .getString("juploadr.ui.dialog.error.communication") //$NON-NLS-1$
                                + e.getMessage());
                    }
                }
            });
        }
    }

    public boolean executeMethod(FlickrMethod method) {
        try {
            method.execute();
            return true;
        } catch (ProtocolException e) {
        } catch (CommunicationException e) {
        }
        if (method instanceof PostUploadAction) {
            addFailedAction((PostUploadAction) method);
        }
        return false;
    }

}